#define CHECK_FLAG(flags,bit)   ((flags) & (1 << (bit)))
#define bytesPerLine(width, depth)	((((width) + 31) >> 5 << 2) * (depth))
#define bytesPerLineRD(width, depth)	((((width) >> 5) << 2) * (depth))

bool multiboot_sane(u32int magic_number) {
    return (magic_number == 0x2BADB002);
}

void prepare_video(multiboot_info* multiboot_information) {
	if(CHECK_FLAG(multiboot_information->flags, 2)) {
		char *video = (char*)multiboot_information->cmdline;
		int i, len = strlen(video);
		// cmdline must be something like video=1024x768x32@0xf0000000,4096
		// where 0xf0000000 is the base address and 4096 is the number of bytes per scanline
		// if the number of byte per scanline is not present, it's going to be guessed (X/8)
		for(i = 6; i<len; i++) {
			if (video[i] == '=')
			if (!memcmp(&video[i-6], " video", 6)) {
				video = &video[i+1];
				parse_video_info(video);
				break;
			}
		}
		//memset16((u16int*)video_info.address, 0xFFFF, video_info.width * video_info.height * 2 );
	}
}

void parse_video_info(char *videoConfigLine) {
	char *p = videoConfigLine;
	int i=0;

	typedef struct {
		int  *var;
		char sep;
	} token;

	token tokens[] = {
		{&video_info.width, 'x'},
		{&video_info.height, 'x'},
		{&video_info.depth, '@'},
		{&video_info.address, ','},
		{&video_info.scanLineSize, '\0'}
	};
	
	while(*p) {
		if (*p == tokens[i].sep) {
			*p = 0;
			*tokens[i].var = _atoi(videoConfigLine);
			videoConfigLine = p+1;
			i++;
		}
		p++;
	};
  
	if(i < 5) {
		*tokens[i].var = _atoi(videoConfigLine);
		video_info.scanLineSize = bytesPerLine(video_info.width, video_info.depth);
	}
}

void display_memory_info(multiboot_info* multiboot_information) {
    set_fgbg(LIGHTCYAN, BLACK);
    multiboot_memory_map* mmap = (multiboot_memory_map*)multiboot_information->mmap_addr;
    printf("Memory Map:\n");
    while((void*)mmap < (void*)(multiboot_information->mmap_addr + multiboot_information->mmap_length)) {		
	   if(mmap->type == 1){ //1 means usable memory
		  printf(" - size=%x baseAddrLow=%x baseAddrHigh=%x lenLow=%x lenHigh=%x type=%x\n",
			 mmap->size, mmap->base_addr_low, mmap->base_addr_high,
			 mmap->length_low, mmap->length_high, mmap->type);
	   }
	   mmap = (multiboot_memory_map*) ( (unsigned int)mmap + mmap->size + sizeof(unsigned int) );
    }
    printf("Memory Upper Bound: %x\n", multiboot_information->mem_upper);
    int onStack = 0xCAFE;
    printf("Stack Location:     %x\n", &onStack);
    printf("Test Location:      %x\n", kernel_main);
    printf("Multiboot Location: %x\n", multiboot_information);
}

void display_module_info(multiboot_info* multiboot_information) {
    set_fgbg(LIGHTCYAN, BLACK);
    module* mod = (module*) multiboot_information->mods_addr;
    int count = multiboot_information->mods_count;
    printf("Multiboot Modules:\n");
    if(count == 0) {
	   printf(" - None Loaded\n");
    }
    while(count>0) {
	   printf(" - %s: start=%x end=%x\n", mod->string, mod->mod_start, mod->mod_end);
	   count--;
	   mod++;
    }
}

void* find_oceania_module(multiboot_info* multiboot_information) {
    module* mod = (module*) multiboot_information->mods_addr;
    int count = multiboot_information->mods_count;
    char* test = "oceania.image";
    char* value = 0;
	
    while(count>0) {
	   value = (char*)mod->string;
	   value = value + strlen(value) - strlen(test);
	   if(strcmp(value, test) == 0) {
		  return (void*)mod->mod_start;
	   }
	   count--;
	   mod++;
    }
    
    return 0;
}
